use serde::Serialize;
use actix::{Response, ResponseFuture, fut};
use actix_http::body::Body;
use actix_web::{Responder, HttpResponse, HttpRequest};
use actix_http::http::header::ContentType;
use std::convert::{TryInto, TryFrom};
use futures::{future, IntoFuture};
use futures::future::FutureResult;

pub mod user;

const API_CONTENT_TYPE: &'static str = "text/json;charset=utf-8";

#[derive(Serialize)]
pub struct ApiFormat<'a, S: Serialize> {
    code: u32,
    message: &'a str,
    data: Option<S>,
}

type InnerApiResult<S> = Result<S, failure::Error>;

pub struct ApiResult<S>(InnerApiResult<S>);

impl<S> ApiResult<S> {
    fn new(result: InnerApiResult<S>) -> Self {
        ApiResult(result)
    }

    fn ok(s: S) -> Self {
        Self(Ok(s))
    }

    fn with<F>(f: F) -> Self where F: Fn() -> InnerApiResult<S> {
        Self(f())
    }
}

impl<S> Into<InnerApiResult<S>> for ApiResult<S> {
    fn into(self) -> InnerApiResult<S> {
        self.0
    }
}

impl<S: Serialize> Responder for ApiResult<S> {
    type Error = actix_http::Error;
    type Future = FutureResult<actix_http::Response, Self::Error>;

    fn respond_to(self, _: &HttpRequest) -> Self::Future {
        let mut error_message: String;

        let api_format = match self.0 {
            Ok(body) => {
                ApiFormat {
                    code: 200,
                    message: "SUCCESS",
                    data: Some(body),
                }
            }
            Err(e) => {
                error_message = format!("{}", e);

                ApiFormat {
                    code: 500,
                    message: &error_message,
                    data: None,
                }
            }
        };
        let content = serde_json::to_string(&api_format).unwrap_or_default();
        future::ok(HttpResponse::Ok().set_header("Content-Type", API_CONTENT_TYPE).body(content))
    }
}

trait HasData<S> {
    fn data(&self) -> S;
}
